package com.wenx.v3gateway.filter;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.micrometer.tracing.Span;
import io.micrometer.tracing.TraceContext;
import io.micrometer.tracing.Tracer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;

/**
 * Gateway统一Trace传播过滤器
 * 
 * <p>负责在Gateway层统一生成和传播traceId，确保同一个请求在不同服务间使用相同的traceId</p>
 * <p>优先级设置为最高，确保在其他过滤器之前执行</p>
 * 
 * @author wenx
 * @version 1.0
 */
@Slf4j
@Component
public class GatewayTraceFilter implements GlobalFilter, Ordered {

    private static Tracer tracer;
    
    /**
     * 统一Trace ID请求头键名
     */
    public static final String UNIFIED_TRACE_ID_HEADER = "X-Trace-Id";
    
    /**
     * 请求ID请求头键名（兼容性）
     */
    public static final String REQUEST_ID_HEADER = "request-id";
    
    /**
     * 用户ID请求头键名
     */
    public static final String USER_ID_HEADER = "X-User-Id";
    
    /**
     * 用户名请求头键名
     */
    public static final String USERNAME_HEADER = "X-Username";
    
    /**
     * 租户ID请求头键名
     */
    public static final String TENANT_ID_HEADER = "X-Tenant-Id";
    
    /**
     * 组织ID请求头键名
     */
    public static final String DEPT_ID_HEADER = "X-Dept-Id";
    
    /**
     * JWT密钥（应该从配置文件读取，这里为演示使用固定值）
     */
    private static final String JWT_SECRET = "mySecretKeymySecretKeymySecretKeymySecretKey";
    
    @Autowired(required = false)
    public void setTracer(Tracer tracer) {
        GatewayTraceFilter.tracer = tracer;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        // 获取或生成统一的traceId
        String unifiedTraceId = getUnifiedTraceId(request);
        
        // 从JWT中解析用户认证信息
        UserAuthInfo authInfo = parseJwtUserInfo(request);
        
        // 构建新的请求，添加统一的trace和认证信息到请求头
        ServerHttpRequest.Builder requestBuilder = request.mutate();
        
        // 设置统一的trace ID到请求头（确保下游服务使用相同的traceId）
        if (StrUtil.isNotBlank(unifiedTraceId)) {
            requestBuilder.header(UNIFIED_TRACE_ID_HEADER, unifiedTraceId);
            requestBuilder.header(REQUEST_ID_HEADER, unifiedTraceId);
        }
        
        // 传递用户认证信息到下游服务
        if (StrUtil.isNotBlank(authInfo.getUserId())) {
            requestBuilder.header(USER_ID_HEADER, authInfo.getUserId());
        }
        if (StrUtil.isNotBlank(authInfo.getUsername())) {
            requestBuilder.header(USERNAME_HEADER, authInfo.getUsername());
        }
        if (StrUtil.isNotBlank(authInfo.getTenantId())) {
            requestBuilder.header(TENANT_ID_HEADER, authInfo.getTenantId());
        }
        if (StrUtil.isNotBlank(authInfo.getDepartmentId())) {
            requestBuilder.header(DEPT_ID_HEADER, authInfo.getDepartmentId());
        }
        
        // 创建新的exchange
        ServerWebExchange newExchange = exchange.mutate()
                .request(requestBuilder.build())
                .build();
        
        // 记录trace传播信息
        if (log.isDebugEnabled()) {
            log.debug("Gateway统一Trace传播 - TraceId: {}, UserId: {}, Path: {}", 
                    unifiedTraceId, authInfo.getUserId(), request.getURI().getPath());
        }
        
        // 继续过滤器链
        return chain.filter(newExchange);
    }
    
    /**
     * 检查Jaeger是否可用
     * 
     * @return 是否可用
     */
    private static boolean isJaegerAvailable() {
        return tracer != null;
    }
    
    /**
     * 获取当前Jaeger Trace ID
     * 
     * @return Jaeger trace ID，如果不存在则返回null
     */
    private static String getJaegerTraceId() {
        if (!isJaegerAvailable()) {
            return null;
        }
        
        try {
            Span currentSpan = tracer.currentSpan();
            if (currentSpan != null) {
                TraceContext context = currentSpan.context();
                return context.traceId();
            }
        } catch (Exception e) {
            log.debug("获取Jaeger trace ID失败", e);
        }
        
        return null;
    }
    
    /**
     * 获取统一的Trace ID（优先使用Jaeger）
     * 
     * @param request HTTP请求对象
     * @return 统一的trace ID
     */
    private static String getUnifiedTraceId(ServerHttpRequest request) {
        // 优先使用Jaeger的trace ID
        String jaegerTraceId = getJaegerTraceId();
        if (StrUtil.isNotBlank(jaegerTraceId)) {
            return jaegerTraceId;
        }
        
        // 回退到传统的trace ID生成
        // 优先从请求头获取追踪ID
        String[] traceHeaders = {"request-id", "X-Trace-Id", "X-Request-Id", "traceId", "trace-id"};
        for (String header : traceHeaders) {
            String traceId = request.getHeaders().getFirst(header);
            if (StrUtil.isNotBlank(traceId)) {
                return traceId;
            }
        }
        
        // 生成新的追踪ID
        return IdUtil.fastSimpleUUID();
    }
    
    /**
     * 从请求中提取用户认证信息
     * 
     * @param request HTTP请求对象
     * @return 用户认证信息
     */
    private static UserAuthInfo extractUserAuthInfo(ServerHttpRequest request) {
        UserAuthInfo authInfo = new UserAuthInfo();
        
        // 提取用户ID
        String userId = request.getHeaders().getFirst(USER_ID_HEADER);
        if (StrUtil.isBlank(userId)) {
            userId = request.getQueryParams().getFirst("userId");
        }
        authInfo.setUserId(userId);
        
        // 提取用户名
        String username = request.getHeaders().getFirst(USERNAME_HEADER);
        authInfo.setUsername(username);
        
        // 提取租户ID
        String tenantId = request.getHeaders().getFirst(TENANT_ID_HEADER);
        authInfo.setTenantId(tenantId);
        
        // 提取组织ID
        String departmentId = request.getHeaders().getFirst(DEPT_ID_HEADER);
        authInfo.setDepartmentId(departmentId);
        
        return authInfo;
    }
    
    /**
     * 从JWT Token中解析用户信息
     * 
     * @param request HTTP请求对象
     * @return 用户认证信息
     */
    private UserAuthInfo parseJwtUserInfo(ServerHttpRequest request) {
        UserAuthInfo authInfo = new UserAuthInfo();
        
        // 从Authorization头获取JWT Token
        String authHeader = request.getHeaders().getFirst("Authorization");
        if (StrUtil.isBlank(authHeader) || !authHeader.startsWith("Bearer ")) {
            // 如果没有JWT，尝试从现有请求头获取用户信息（兼容性）
            return extractUserAuthInfo(request);
        }
        
        try {
            String token = authHeader.substring(7); // 移除"Bearer "前缀
            
            // 解析JWT
             SecretKey key = Keys.hmacShaKeyFor(JWT_SECRET.getBytes(StandardCharsets.UTF_8));
             Claims claims = Jwts.parser()
                     .setSigningKey(key)
                     .build()
                     .parseClaimsJws(token)
                     .getBody();
            
            // 提取用户信息
            String userId = claims.get("user_id", String.class);
            String username = claims.get("user_name", String.class);
            String tenantId = claims.get("tenant_id", String.class);
            String departmentId = claims.get("department_id", String.class);
            authInfo.setUserId(userId);
            authInfo.setUsername(username);
            authInfo.setTenantId(tenantId);
            authInfo.setDepartmentId(departmentId);
            
            if (log.isDebugEnabled()) {
                log.debug("JWT解析成功 - UserId: {}, Username: {}, TenantId: {}", 
                        userId, username, tenantId);
            }
            
        } catch (Exception e) {
            log.warn("JWT解析失败，使用默认用户信息: {}", e.getMessage());
            // JWT解析失败时，尝试从请求头获取用户信息
            return extractUserAuthInfo(request);
        }
        
        return authInfo;
    }
    
    /**
     * 提取客户端IP地址
     * 
     * @param request HTTP请求对象
     * @return 客户端IP地址
     */
    public static String extractClientIp(ServerHttpRequest request) {
        // 尝试从各种可能的请求头中获取真实IP
        String[] ipHeaders = {
            "X-Forwarded-For",
            "X-Real-IP", 
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_CLIENT_IP",
            "HTTP_X_FORWARDED_FOR"
        };
        
        for (String header : ipHeaders) {
            String ip = request.getHeaders().getFirst(header);
            if (StrUtil.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
                // X-Forwarded-For可能包含多个IP，取第一个
                if (ip.contains(",")) {
                    ip = ip.split(",")[0].trim();
                }
                if (isValidIp(ip)) {
                    return ip;
                }
            }
        }
        
        // 最后尝试从远程地址获取
        if (request.getRemoteAddress() != null) {
            return request.getRemoteAddress().getAddress().getHostAddress();
        }
        
        return "unknown";
    }
    
    /**
     * 验证IP地址格式
     * 
     * @param ip IP地址字符串
     * @return 是否为有效IP
     */
    private static boolean isValidIp(String ip) {
        if (StrUtil.isBlank(ip)) {
            return false;
        }
        
        // 排除内网保留地址和无效地址
        if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || "localhost".equals(ip)) {
            return false;
        }
        
        // 简单的IP格式验证
        String[] parts = ip.split("\\.");
        if (parts.length != 4) {
            return false;
        }
        
        try {
            for (String part : parts) {
                int num = Integer.parseInt(part);
                if (num < 0 || num > 255) {
                    return false;
                }
            }
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    /**
     * 创建包含追踪和认证信息的上下文字符串
     * 
     * @param request HTTP请求对象
     * @param traceId 追踪ID
     * @param authInfo 用户认证信息
     * @return 上下文字符串
     */
    public static String getTraceAndAuthContext(ServerHttpRequest request, String traceId, UserAuthInfo authInfo) {
        String clientIp = extractClientIp(request);
        
        return String.format("[%s|%s|%s|%s|%s]",
            StrUtil.isNotBlank(traceId) ? traceId : "unknown",
            StrUtil.isNotBlank(authInfo.getUserId()) ? authInfo.getUserId() : "anonymous",
            StrUtil.isNotBlank(authInfo.getUsername()) ? authInfo.getUsername() : "unknown",
            StrUtil.isNotBlank(authInfo.getTenantId()) ? authInfo.getTenantId() : "default",
            StrUtil.isNotBlank(clientIp) ? clientIp : "unknown"
        );
    }

    @Override
    public int getOrder() {
        // 设置最高优先级，确保在其他过滤器之前执行
        return Ordered.HIGHEST_PRECEDENCE;
    }
    
    /**
     * 用户认证信息类
     */
    public static class UserAuthInfo {
        private String userId;
        private String username;
        private String tenantId;
        private String departmentId;
        
        // Getters and Setters
        public String getUserId() { return userId; }
        public void setUserId(String userId) { this.userId = userId; }
        
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        
        public String getTenantId() { return tenantId; }
        public void setTenantId(String tenantId) { this.tenantId = tenantId; }
        
        public String getDepartmentId() { return departmentId; }
        public void setDepartmentId(String departmentId) { this.departmentId = departmentId; }
    }
}